/*
 * Copyright 2020-2022 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "board_init.h"
#include "demo_config.h"
#include "demo_info.h"
#include "fsl_debug_console.h"
#include "audio.h"
#include "model.h"
#include "output_postproc.h"
#include "timer.h"

extern "C"{
#include "lvgl_support.h"
#include "pin_mux.h"
#include "lvgl.h"
#include "gui_guider.h"
#include "events_init.h"
}
/*
    LED helper
*/
#include "fsl_gpio.h"
#include "fsl_iomuxc.h"
#include "board.h"
#define LED_ON() GPIO_PinWrite(BOARD_USER_LED_GPIO, BOARD_USER_LED_GPIO_PIN, 0);
#define LED_OFF() GPIO_PinWrite(BOARD_USER_LED_GPIO, BOARD_USER_LED_GPIO_PIN, 1); 

void init_led(){
    IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_09_GPIO1_IO09, 0U); 
    gpio_pin_config_t config = {
        .direction = kGPIO_DigitalOutput,
        .outputLogic = 1,
    };
    GPIO_PinInit(BOARD_USER_LED_GPIO, BOARD_USER_LED_GPIO_PIN, &config);
}

/*******************************************************************************
 * Definitions
 ******************************************************************************/


// declare the function, all included by extern "C"
extern "C"{
    int scan_wav(void);
    int get_sound_data(int16_t* data, uint32_t wanted_len);
}

#define AUDIO_LEN  (40 * 16)  /* 40ms, 16000Hz */

lv_ui guider_ui;
float snore_score;
volatile bool s_lvglTaskPending = false;
bool all_process_begin = false;
uint8_t detection_threshold = 50;
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
#if LV_USE_LOG
static void print_cb(const char *buf);
#endif

static int16_t audio_data[AUDIO_LEN];

AT_QUICKACCESS_SECTION_CODE(void BOARD_ReconfigFlexSpiRxBuffer(void));

/*
 * When PXP fetch images from FlexSPI flash, the default FlexSPI RX buffer
 * configuration does not meet the PXP bandwidth requirement. Reconfigure
 * here.
 */
void BOARD_ReconfigFlexSpiRxBuffer(void)
{
    uint32_t ahbcr;

    /* Disable I cache and D cache */
    if (SCB_CCR_IC_Msk == (SCB_CCR_IC_Msk & SCB->CCR))
    {
        SCB_DisableICache();
    }

    if (SCB_CCR_DC_Msk == (SCB_CCR_DC_Msk & SCB->CCR))
    {
        SCB_DisableDCache();
    }

    ahbcr = FLEXSPI->AHBCR;

    /* Temporarily disable prefetching while changing the buffer settings */
    FLEXSPI->AHBCR = ahbcr & ~(FLEXSPI_AHBCR_CACHABLEEN_MASK | FLEXSPI_AHBCR_PREFETCHEN_MASK);

    /* Wait for FlexSPI idle to make sure no flash data transfer. */
    while ((FLEXSPI->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) == 0U)
    {
    }

    /* Allocate half of the prefetch buffer to the core */
    FLEXSPI->AHBRXBUFCR0[0] =
        FLEXSPI_AHBRXBUFCR0_PREFETCHEN_MASK | FLEXSPI_AHBRXBUFCR0_MSTRID(0) | FLEXSPI_AHBRXBUFCR0_BUFSZ(0x40);

    /* Disable dedicate prefetch buffer for DMA. */
    FLEXSPI->AHBRXBUFCR0[1] =
        FLEXSPI_AHBRXBUFCR0_PREFETCHEN_MASK | FLEXSPI_AHBRXBUFCR0_MSTRID(1) | FLEXSPI_AHBRXBUFCR0_BUFSZ(0x00);

    /* Disable dedicate prefetch buffer for DCP. */
    FLEXSPI->AHBRXBUFCR0[2] =
        FLEXSPI_AHBRXBUFCR0_PREFETCHEN_MASK | FLEXSPI_AHBRXBUFCR0_MSTRID(2) | FLEXSPI_AHBRXBUFCR0_BUFSZ(0x00);

    /* Other half of the buffer for other masters incl. PXP */
    FLEXSPI->AHBRXBUFCR0[3] =
        FLEXSPI_AHBRXBUFCR0_PREFETCHEN_MASK | FLEXSPI_AHBRXBUFCR0_MSTRID(3) | FLEXSPI_AHBRXBUFCR0_BUFSZ(0x40);

    FLEXSPI->AHBCR = ahbcr; /* Set AHBCR back to the original value */

    /* Enable I cache and D cache */
    SCB_EnableDCache();
    SCB_EnableICache();
}

#if LV_USE_LOG
static void print_cb(const char *buf)
{
    PRINTF("\r%s\n", buf);
}
#endif

int main(void)
{
		BOARD_Init();
	
		BOARD_ReconfigFlexSpiRxBuffer();

    TIMER_Init();
    
    DEMO_PrintInfo();
	
		lv_port_pre_init();
    lv_init();
    lv_port_disp_init();
    lv_port_indev_init();

    setup_ui(&guider_ui);
    events_init(&guider_ui);
		
    init_led();
    LED_ON();
    scan_wav();
    LED_OFF();
		
    // a strange code, insert this line with o0, will not be valid. 
    // while(1){}
		
    if (MODEL_Init() != kStatus_Success)
    {
        PRINTF("Failed initializing model" EOL);
        for (;;) {}
    }
		
		//waiting for button pressed
		while(!all_process_begin){
			while (!s_lvglTaskPending)
			{
			}
			s_lvglTaskPending = false;
			lv_task_handler();
		}

    tensor_dims_t inputDims;
    tensor_type_t inputType;
    uint8_t* inputData = MODEL_GetInputTensorData(&inputDims, &inputType);

    tensor_dims_t outputDims;
    tensor_type_t outputType;
    uint8_t* outputData = MODEL_GetOutputTensorData(&outputDims, &outputType);

    while (1)
    {		
        /* Expected tensor dimensions: [batches, frames, mfcc, channels] */
        if (AUDIO_GetSpectralSample(inputData, inputDims.data[1] * inputDims.data[2]) != kStatus_Success)
        {
            PRINTF("Failed retrieving input audio" EOL);
            for (;;) {}
        }

        auto startTime = TIMER_GetTimeInUS();
        MODEL_RunInference();
        auto endTime = TIMER_GetTimeInUS();

        MODEL_ProcessOutput(outputData, &outputDims, outputType, endTime - startTime);
        
        snore_score = (((int8_t*)outputData)[1] + 128.f) / 256.f;
        if(snore_score >= (detection_threshold / 100.f)){
					lv_led_on(guider_ui.red_led);
					lv_led_off(guider_ui.green_led);
					
					lv_obj_set_style_text_font(guider_ui.label_confidence, &lv_font_montserratMedium_14, LV_PART_MAIN);
					lv_obj_set_style_text_color(guider_ui.label_confidence, lv_color_hex(0x000080), LV_PART_MAIN);
					lv_obj_set_style_bg_opa(guider_ui.label_confidence, 0, LV_PART_MAIN);
					
					lv_label_set_text(guider_ui.label_confidence, "Confidence:");			
					lv_label_set_text_fmt(guider_ui.label_con_num, "%d", (int)(snore_score*100));
					lv_label_set_text(guider_ui.label_con_pct, "%");
					
					lv_obj_set_style_bg_opa(guider_ui.label_snoring, 26, LV_PART_MAIN);
					lv_label_set_text(guider_ui.label_snoring, "Snoring !");
        }else{
					lv_led_on(guider_ui.green_led);
					lv_led_off(guider_ui.red_led);
					
					lv_obj_set_style_text_font(guider_ui.label_confidence, &lv_font_Acme_Regular_16, LV_PART_MAIN);
					lv_obj_set_style_text_color(guider_ui.label_confidence, lv_color_hex(0x006326), LV_PART_MAIN);
					lv_obj_set_style_bg_color(guider_ui.label_confidence, lv_color_hex(0x11eb5f),LV_PART_MAIN);
					lv_obj_set_style_bg_opa(guider_ui.label_confidence, 102, LV_PART_MAIN);

					lv_label_set_text(guider_ui.label_confidence, "Breathe Well !");
					lv_label_set_text(guider_ui.label_con_num, " ");
					lv_label_set_text(guider_ui.label_con_pct, " ");
					
					lv_obj_set_style_bg_opa(guider_ui.label_snoring, 0, LV_PART_MAIN);
					lv_label_set_text(guider_ui.label_snoring, " ");
        }

				lv_task_handler();

    }
}
